package org.dreamwork.jasmine2.engine;

import org.dreamwork.misc.Base64;
import org.dreamwork.misc.MimeType;
import org.dreamwork.util.FileInfo;
import org.dreamwork.util.StringUtil;
import org.dreamwork.util.VirtualFile;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Date;

/**
 * Created by IntelliJ IDEA.
 * User: seth.yang
 * Date: 2010-11-5
 * Time: 14:59:36
 */
public class DownloadManager extends HttpServlet {
    public static File downloadDir;
    private static final int MAX_LENGTH = 10240;

    @Override
    public void init () throws ServletException {
        ServletContext context = getServletContext ();
        ServletConfig config = getServletConfig ();
        String location = config.getInitParameter ("basedir");
        File basedir;
        if (location != null)
            basedir = new File (location);
        else
            basedir = (File) context.getAttribute (JasmineEngine.J2EE_TEMP_DIR);
        downloadDir = new File (basedir, "downloads");
        if (!downloadDir.exists () && downloadDir.mkdirs ())
            throw new ServletException ("Can't create folder " + downloadDir);
    }

    @Override
    protected void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpContext context = new HttpContext ();
        context.setApplication (getServletContext ());
        context.setRequest (request);
        context.setResponse (response);

        String path = request.getServletPath ();
        String contextPath = request.getContextPath ();
        String uri = URLDecoder.decode (request.getRequestURI (), "utf-8");
        String prefix = contextPath + "/" + path;
        prefix = prefix.replace ("//", "/");

        String realFileName = uri.substring (prefix.length ());
        if (realFileName.charAt (0) == '/') realFileName = realFileName.substring (1);
        int pos = realFileName.lastIndexOf ('/');
        String readableFileName;
        if (pos != -1) {
            readableFileName = decode (realFileName.substring (pos + 1));
        } else
            readableFileName = decode (realFileName);

        File file = new File (downloadDir, realFileName);

        if (!file.exists ()) {
            response.sendError (HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        if (!file.canRead ()) {
            response.sendError (HttpServletResponse.SC_FORBIDDEN);
            return;
        }

        if (pos != -1) realFileName = realFileName.substring (pos + 1);

        String contentType = "application/octet-stream";
        MimeType type = FileInfo.getMimeType (readableFileName);
        response.setContentType (type == null ? contentType : type.getName ());

        String fileName;
        Browser browser = context.getBrowser ();
        if (browser.isIE) {
            fileName = URLEncoder.encode (readableFileName, "utf-8");
            fileName = fileName.replace ("+", "%20");
        } else {
//            fileName = "=?UTF-8?B?" + realFileName.replace ('$', '/').replace ('-', '+') + "?=";
            fileName = new String (readableFileName.getBytes (), "iso-8859-1");
        }

        response.setHeader ("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
        response.setHeader ("Content-Length", String.valueOf (file.length ()));
        response.setHeader ("Last-Modified", new Date (file.lastModified ()).toString ());
        response.setHeader ("X-Powered-By", "Jasmine2");

        int length = Math.max (MAX_LENGTH, (int)file.length ());
        byte[] buff = new byte[length];
        FileInputStream fis = new FileInputStream (file);
        OutputStream os = response.getOutputStream ();
        try {
            while ((length = fis.read (buff)) != -1)
                os.write (buff, 0, length);
        } finally {
            fis.close ();
            os.flush ();
//            response.flushBuffer ();
        }
    }

    public static void save (VirtualFile vfile, String path) throws IOException {
        save (vfile, path, vfile.getName ());
    }

    public static void save (VirtualFile vfile, String path, String name) throws IOException {
        File folder = StringUtil.isEmpty (path) ? downloadDir : new File (downloadDir, path);
        if (!folder.exists () && !folder.mkdirs ())
            throw new IOException ("Can't create folder " + folder.getCanonicalPath ());

        String fileName = folder.getCanonicalPath () + "/" + encode (name);
        vfile.saveAs (fileName);
    }

    public static String encode (String name) throws UnsupportedEncodingException {
        return new String (Base64.encode (name.getBytes ("utf-8")), "utf-8").replace ('+', '-').replace ('/', '$');
    }

    public static String decode (String encoded) throws UnsupportedEncodingException {
        String text = encoded.replace ('$', '/').replace ('-', '+');
        return new String (Base64.decode (text.getBytes ("utf-8")), "utf-8");
    }
}